home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / util / misc / fonewd.lha / phonewrd.c < prev    next >
C/C++ Source or Header  |  1992-09-02  |  22KB  |  922 lines

  1. /*
  2.    Phonewrd - given a phone number, find all words or phrases in a dictionary
  3.      which fit it.
  4.  
  5.    version 1.1 - 4/5/93
  6.  
  7. Ported To Amiga.
  8.     Christopher Klaus, cklaus@hotsun.nersc.gov or gt6468c@hydra.gatech.edu
  9.     26468 GaTech Station
  10.     Atlanta, GA 30332
  11.     (404)-676-9622
  12.  
  13.  
  14.    (c) copyright 1993, Eric Haines, all rights reserved (erich@eye.com)
  15.  
  16.    See the man page for details.
  17.  */
  18.  
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <string.h>
  22.  
  23. /*============= default related =========================================== */
  24.  
  25. /* path to dictionary */
  26. #define    DICT_PATH    "dict"
  27.  
  28. /* number of digits in phone number */
  29. #define    NUM_DIGITS    7
  30.  
  31. /* how many numerals are allowed in our phrase? */
  32. #define    NUMERALS_ALLOWED    0
  33.  
  34. /* minimum length of words: note that 1 always gets bumped to 2, because of
  35.  * OneLetter below.
  36.  */
  37. #define    MIN_LENGTH    1
  38.  
  39. /* one letter words that are acceptable */
  40. #define    ONE_LETTER    "aio"
  41. /* If you like things like "CDB" (see the bee) or "IM4U, use or modify the
  42.  * array below.  Yes, "j" and "k" are potential names, "m" is only in "I am",
  43.  * "q" is queue, etc - you decide...  This option tends to generate lots of
  44.  * useless goop, but it's good if you're desperate.
  45.  */
  46. /* #define ONE_LETTER    "abcdgimoptuxy248" */
  47.  
  48. #define    NO_NUMBER    -2
  49.  
  50. /* to make Q and Z not be anything, set them to NO_NUMBER, else set them
  51.  * to the digit you want (e.g. 0 or 1)
  52.  */
  53. #define    Q    NO_NUMBER
  54. #define    Z    NO_NUMBER
  55.  
  56. /* translation table for letters to numbers */
  57. int    Letter2Numeral[] =
  58.     /* a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z */
  59.      { 2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,Q,7,7,8,8,8,9,9,9,Z } ;
  60.  
  61.  
  62. /*============= storage space related ===================================== */
  63.  
  64. #define MAX_DIGITS    14
  65. #define    MAX_INDICES    (((MAX_DIGITS+1)*MAX_DIGITS)/2)
  66.  
  67. /* increment for word list memory space */
  68. #define    WORD_LIST_SIZE    50
  69.  
  70. /*============= internal use constants ==================================== */
  71.  
  72. #define    FIXED_LETTER    -1
  73.  
  74. #define    BUFSIZE        256
  75. #ifndef    TRUE
  76. #define    TRUE    1
  77. #define    FALSE    0
  78. #endif
  79.  
  80. /*============= structures ================================================ */
  81.  
  82. /* array of words, indexed by digit location and length of word.  This list is
  83.  * built up in the first part of the program, as each dictionary word is
  84.  * categorized and put in its list.  Then this structure is traversed to make
  85.  * up the combinations.
  86.  */
  87. typedef    struct {
  88.     char    **p_word ;    /* list of pointers to words */
  89.     int    count ;        /* current count */
  90.     int    size ;        /* allocated size */
  91. } wordlist, *p_wordlist ;
  92.  
  93. /*============= data and macros =========================================== */
  94.  
  95. #define    IndexWL( digit_loc, length )    ( WLoffset[digit_loc] + length - 1 )
  96.  
  97. wordlist WL[MAX_INDICES] ;
  98. int    WLoffset[MAX_DIGITS] ;
  99. int    PhoneNum[MAX_DIGITS+1] ;    /* phone number translation */
  100. char    PhoneStr[MAX_DIGITS+1] ;    /* phone number letter, if specified */
  101. int    NumeralMapped[10] ;        /* if TRUE, then index # is mapped */
  102. char    OneLetter[BUFSIZE] ;
  103. char    DictPath[BUFSIZE] ;
  104. char    *ProgName ;
  105.  
  106. int    NumDigits ;
  107. int    NumIndices ;
  108. int    NumNumerals ;
  109. int    MinLength ;
  110. int    RollOwn ;
  111. int    Concat ;
  112. int    Verbose ;
  113. int    MatchTot ;
  114. int    Wildcard ;
  115. int    TotBrk ;
  116.  
  117. int    HoldSize ;
  118. int    HoldCount ;
  119. char    **HoldWord ;
  120.  
  121. char    OutputGrid[MAX_DIGITS][27] ;
  122.  
  123. #define    USAGE    fprintf( stderr, "usage: %s [options] phone#[*...]\n"    \
  124.     " [*...] - extra *'s at the end mean optional wildcard letters\n" \
  125.     "  -l # - minimum length of words (cur. == %d)\n"        \
  126.     "  -n # - number of numerals allowed in phrase (cur. == %d)\n"    \
  127.     "  -q # - mapping of q (cur. == %d)\n"                \
  128.     "  -z # - mapping of z (cur. == %d)\n"                \
  129.     "  -d string - dictionary path (cur. == %s)\n"            \
  130.     "  -s string - allowed single letter words (cur. == \"%s\")\n"    \
  131.     "  -m char[26] - mapping of entire alphabet\n"            \
  132.     "  -r - output the corresponding letters in an array\n"        \
  133.     "  -c - output all combinations (no dictionary)\n"        \
  134.     "  -v - verbose output (show words that do fit)\n"        \
  135.     , ProgName                            \
  136.     , MinLength                            \
  137.     , NumNumerals                            \
  138.     , Q                                \
  139.     , Z                                \
  140.     , DictPath                            \
  141.     , OneLetter                            \
  142.     ) ;
  143.  
  144. /*============= procedure declarations ==================================== */
  145.  
  146. int phone_check() ;
  147. void roll_own() ;
  148. void concat_it() ;
  149. void concat_letter_out() ;
  150. void concat_letter_breaks_out() ;
  151. void init_wl() ;
  152. int fit_word() ;
  153. void search_for_match() ;
  154. void hold_word() ;
  155. void store_word() ;
  156. void free_wl() ;
  157. int scan_options() ;
  158.  
  159. /*============= procedures ================================================ */
  160.  
  161. main(argc,argv)
  162. int argc;  char *argv[];
  163. {
  164. int    nargc ;
  165. char    *nargv[BUFSIZE], *targv ;
  166. int    i, hdr_out ;
  167.  
  168.     ProgName = argv[1] ;
  169.  
  170.     HoldSize = HoldCount = 0 ;
  171.  
  172.     NumNumerals = NUMERALS_ALLOWED ;
  173.     MinLength = MIN_LENGTH ;
  174.     RollOwn = FALSE ;
  175.     Concat = 0 ;
  176.     Verbose = FALSE ;
  177.  
  178.     strcpy( OneLetter, ONE_LETTER ) ;
  179.     strcpy( DictPath, DICT_PATH ) ;
  180.  
  181.     /* translate phone number */
  182.     if ( argc < 2 ) {
  183.     fprintf( stderr, "Not enough arguments.\n" ) ;
  184.     USAGE ;
  185.     return( 1 ) ;
  186.     }
  187.  
  188.     if ( !scan_options( argc, argv, &nargc, nargv ) ) {
  189.     return( 1 ) ;
  190.     }
  191.  
  192.     if ( nargc < 1 ) {
  193.     fprintf( stderr, "No phone number found.\n" ) ;
  194.     USAGE ;
  195.     return( 1 ) ;
  196.     }
  197.  
  198.     /* loop through phone number(s), output headers if more than one */
  199.     hdr_out = ( nargc > 1 ) ;
  200.     for ( i = 0 ; i < nargc ; i++ ) {
  201.     if ( i ) {
  202.         /* output carriage returns before 2nd, 3rd, etc */
  203.         fprintf( stdout, "\n" ) ;
  204.     }
  205.     targv = nargv[i] ;
  206.     if ( hdr_out ) {
  207.         fprintf( stdout, "# %s\n", targv ) ;
  208.     }
  209.     if ( phone_check( targv ) ) {
  210.         /* something very bad happened, so quit */
  211.         return( 1 ) ;
  212.     }
  213.     }
  214.  
  215.     return( 0 ) ;
  216. }
  217.  
  218. int
  219. phone_check( numeral_string )
  220. char    *numeral_string ;
  221. {
  222. int    i, tot, word_count, length, min_length, found_digit, val ;
  223. char    tchr, *tstr ;
  224. char    dict_word[BUFSIZE] ;
  225. char    out_string[BUFSIZE] ;
  226. p_wordlist    p_wl ;
  227. FILE    *infile ;
  228.  
  229.     NumDigits = 0 ;
  230.     TotBrk = -1 ;
  231.  
  232.     tstr = numeral_string ;
  233.     tchr = *tstr++ ;
  234.     found_digit = FALSE ;
  235.     while ( tchr != '\0' && tchr != '*' && (NumDigits <= MAX_DIGITS ) ) {
  236.     if ( ( tchr >= '0' ) && ( tchr <= '9' ) ) {
  237.         /* convert to digit */
  238.         PhoneNum[NumDigits] = (int)(tchr - '0') ;
  239.         PhoneStr[NumDigits++] = tchr ;
  240.         found_digit = TRUE ;
  241.     } else if ( ( (tchr >= 'A') && (tchr <= 'Z') ) ||
  242.             ( (tchr >= 'a') && (tchr <= 'z') ) ) {
  243.         PhoneNum[NumDigits] = FIXED_LETTER ;
  244.         PhoneStr[NumDigits++] = tolower( tchr ) ;
  245.     }
  246.     tchr = *tstr++ ;
  247.     }
  248.     PhoneStr[NumDigits]='\0' ;
  249.  
  250.     if ( NumDigits <= 0 ) {
  251.     fprintf( stderr, "Sorry, no digits input in `%s'\n", numeral_string ) ;
  252.     fprintf( stderr, "Recompile with a higher MAX_DIGITS setting.\n" ) ;
  253.     USAGE ;
  254.     return(0) ;
  255.     }
  256.  
  257.     /* check if we're in roll own mode */
  258.     if ( RollOwn ) {
  259.     roll_own() ;
  260.     return(0) ;
  261.     }
  262.  
  263.     /* check if we're in concat output mode */
  264.     if ( Concat ) {
  265.     concat_it() ;
  266.     return(0) ;
  267.     }
  268.  
  269.     /* check if any numbers were input */
  270.     if ( !found_digit ) {
  271.     /* entirely alphabetic input, so translate to number */
  272.     for ( i = 0 ; i < NumDigits ; i++ ) {
  273.         val = Letter2Numeral[PhoneStr[i]-'a'] ;
  274.         if ( val != NO_NUMBER ) {
  275.         fprintf( stdout, "%d", val ) ;
  276.         } else {
  277.         fprintf( stdout, "*" ) ;
  278.         }
  279.     }
  280.     fprintf( stdout, "\n" ) ;
  281.     return(0) ;
  282.     }
  283.  
  284.     if ( tchr == '*' ) {
  285.     /* the phone number ends in a wild card - phrases can be longer */
  286.     Wildcard = 1 ;
  287.     while ( *tstr++ == '*' ) {
  288.         Wildcard++ ;
  289.     }
  290.     } else {
  291.     Wildcard = 0 ;
  292.     }
  293.  
  294.     if ( NumDigits > MAX_DIGITS ) {
  295.     fprintf( stderr, "Sorry, too many digits input in `%s'; "
  296.         "only %d allowed.\n", numeral_string, NumDigits ) ;
  297.     USAGE ;
  298.     return(0) ;
  299.     }
  300.  
  301.     /* set up offset array for indexing WL */
  302.     tot = 0 ;
  303.     for ( i = 0 ; i < NumDigits ; i++ ) {
  304.     WLoffset[i] = tot ;
  305.     tot += (NumDigits-i) ;
  306.     }
  307.     NumIndices = ((NumDigits+1)*NumDigits)/2 ;
  308.  
  309.     /* build WL */
  310.     word_count = 0 ;
  311.     init_wl() ;
  312.     infile = fopen( DictPath, "r") ;
  313.     min_length = MinLength > 1 ? MinLength : 2 ;
  314.     if ( infile != NULL ) {
  315.     while ( fgets( dict_word, BUFSIZE, infile ) ) {
  316.         word_count += fit_word( dict_word, min_length ) ;
  317.     }
  318.     } else {
  319.     fprintf( stderr, "Sorry, couldn't find dictionary at `%s'.\n",
  320.         DictPath ) ;
  321.     return(1) ;
  322.     }
  323.     fclose( infile ) ;
  324.  
  325.     if ( Verbose ) {
  326.     for ( i = 0 ; i < NumDigits ; i++ ) {
  327.         for ( length = 1 ; length <= NumDigits-i ; length++ ) {
  328.         p_wl = &WL[IndexWL( i, length )] ;
  329.         if ( p_wl->count ) {
  330.             fprintf( stdout, "\nDigit %d, length %d:\n", i+1, length ) ;
  331.             for ( tot = 0 ; tot < p_wl->count ; tot++ ) {
  332.             fprintf( stdout, " %s\n", p_wl->p_word[tot] ) ;
  333.             }
  334.         }
  335.         }
  336.     }
  337.     }
  338.  
  339.     MatchTot = 0 ;    /* number of words matched */
  340.  
  341.     /* search through WL for paths */
  342.     if ( word_count ) {
  343.     /* good, there's something to work with: search away! */
  344.     *out_string = '\0' ;
  345.     search_for_match( 0, out_string, out_string, 0 ) ;
  346.     if ( !MatchTot ) {
  347.         fprintf( stderr,
  348.         "Bad luck: dictionary words fit, but there's too much "
  349.         "numeral goop.\n" ) ;
  350.         fprintf( stderr,
  351.         "I'd try again with the option `-n %d' or so.\n",
  352.         NumNumerals+1 ) ;
  353.     }
  354.     } else {
  355.     fprintf( stderr,
  356.         "Worst luck: no dictionary words fit anywhere in %s.\n",
  357.         PhoneStr ) ;
  358.     fprintf( stderr,
  359.         "Maybe try again with the option `-s abcdgimoptuxy248'.\n" ) ;
  360.     }
  361.  
  362.     free_wl() ;
  363.  
  364.     return(0) ;
  365. }
  366.  
  367. void
  368. roll_own()
  369. {
  370. int    digit_letter[MAX_DIGITS], found_one, search, i, j, k ;
  371.  
  372.     for ( i = 0 ; i < NumDigits ; i++ ) {
  373.     if ( PhoneNum[i] == FIXED_LETTER ) {
  374.         digit_letter[i] = -2 ;
  375.     } else {
  376.         digit_letter[i] = -1 ;
  377.     }
  378.     }
  379.     found_one = TRUE ;
  380.     for ( i = 0 ; i < 26 && found_one ; i++ ) {
  381.     found_one = FALSE ;
  382.     for ( j = 0 ; j < NumDigits ; j++ ) {
  383.         if ( digit_letter[j] >= 26 ) {
  384.         /* done with this one */
  385.         fprintf( stdout, "   " ) ;
  386.         } else {
  387.         if ( digit_letter[j] == -2 ) {
  388.             found_one = TRUE ;
  389.             digit_letter[j] = 26 ;
  390.             fprintf( stdout, "  %c", PhoneStr[j] ) ;
  391.         } else {
  392.             for ( k = digit_letter[j] + 1, search = TRUE
  393.             ; k < 26 && search
  394.             ; k++ ) {
  395.  
  396.             if ( Letter2Numeral[k] == PhoneNum[j] ) {
  397.                 search = FALSE ;
  398.                 found_one = TRUE ;
  399.                 fprintf( stdout, "  %c", (char)(k+'a') ) ;
  400.                 digit_letter[j] = k ;
  401.             }
  402.             }
  403.             if ( search ) {
  404.             /* no further match found */
  405.             if ( digit_letter[j] < 0 ) {
  406.                 /* numeral is not mapped, so output it directly */
  407.                 fprintf( stdout, "  %c", PhoneStr[j] ) ;
  408.             }
  409.             digit_letter[j] = 26 ;
  410.             }
  411.         }
  412.         }
  413.     }
  414.     fprintf( stdout, "\n" ) ;
  415.     }
  416. }
  417.  
  418. void
  419. concat_it()
  420. {
  421. int    i, j, tot ;
  422. char    full_string[MAX_DIGITS+1] ;
  423.  
  424.     for ( i = 0 ; i < NumDigits ; i++ ) {
  425.     if ( PhoneNum[i] == FIXED_LETTER ) {
  426.         OutputGrid[i][0] = PhoneStr[i] ;
  427.         OutputGrid[i][1] = '\0' ;
  428.     } else {
  429.         tot = 0 ;
  430.         for ( j = 0 ; j < 26 ; j++ ) {
  431.         if ( Letter2Numeral[j] == PhoneNum[i] ) {
  432.             OutputGrid[i][tot++] = (char)(j+'a') ;
  433.         }
  434.         }
  435.         if ( !tot ) {
  436.         /* no match found */
  437.         OutputGrid[i][tot++] = PhoneStr[i] ;
  438.         }
  439.         OutputGrid[i][tot] = '\0' ;
  440.     }
  441.     }
  442.  
  443.     if ( Concat == 1 ) {
  444.     concat_letter_out( 0, full_string ) ;
  445.     } else {
  446.     concat_letter_breaks_out( 0, full_string ) ;
  447.     }
  448. }
  449.  
  450. void
  451. concat_letter_out( digit, full_string )
  452. int    digit ;
  453. char    *full_string ;
  454. {
  455. int    i, length ;
  456. char    *cstr ;
  457.  
  458.     if ( digit >= NumDigits ) {
  459.     full_string[digit] = '\0' ;
  460.     fprintf( stdout, "%s\n", full_string ) ;
  461.     } else {
  462.     length = strlen( cstr = OutputGrid[digit] ) ;
  463.     for ( i = 0 ; i < length ; i++ ) {
  464.         full_string[digit] = *cstr++ ;
  465.         concat_letter_out( digit+1, full_string ) ;
  466.     }
  467.     }
  468. }
  469.  
  470. void
  471. concat_letter_breaks_out( digit, full_string )
  472. int    digit ;
  473. char    *full_string ;
  474. {
  475. char    brk_string[MAX_DIGITS*2] ;
  476. int    i, j, bny, index, length ;
  477. char    *cstr ;
  478.  
  479.     if ( digit >= NumDigits ) {
  480.     if ( TotBrk == -1 ) {
  481.         TotBrk = 1 ;
  482.         for ( i = 1 ; i < NumDigits ; i++ ) {
  483.         TotBrk *= 2 ;
  484.         }
  485.     }
  486.     for ( i = 0 ; i < TotBrk ; i++ ) {
  487.         bny = i ;
  488.         index = 0 ;
  489.         for ( j = 0 ; j < NumDigits ; j++ ) {
  490.         brk_string[index++] = full_string[j] ;
  491.         if ( bny & 0x1 ) {
  492.             brk_string[index++] = ' ' ;
  493.         }
  494.         bny = bny >> 1 ;
  495.         }
  496.         brk_string[index] = '\0' ;
  497.         fprintf( stdout, "%s\n", brk_string ) ;
  498.     }
  499.     } else {
  500.     length = strlen( cstr = OutputGrid[digit] ) ;
  501.     for ( i = 0 ; i < length ; i++ ) {
  502.         full_string[digit] = *cstr++ ;
  503.         concat_letter_breaks_out( digit+1, full_string ) ;
  504.     }
  505.     }
  506. }
  507.  
  508. /* create structures needed for the word list, etc */
  509. void
  510. init_wl()
  511. {
  512. p_wordlist p_wl ;
  513. int    i, j, length, search ;
  514. char    tstr[2] ;
  515.  
  516.     for ( i = 0, p_wl = WL ; i < NumIndices ; i++, p_wl++ ) {
  517.     p_wl->p_word = NULL ;
  518.     p_wl->count = 0 ;
  519.     p_wl->size = 0 ;
  520.     }
  521.  
  522.     /* figure out which numerals don't have any letter translations */
  523.     for ( i = 0 ; i < 10 ; i++ ) {
  524.     for ( j = 0, search = TRUE ; j < 26 && search ; j++ ) {
  525.         if ( i == Letter2Numeral[j] ) {
  526.         search = FALSE ;
  527.         }
  528.     }
  529.     /* set to TRUE if numeral is mapped by something */
  530.     NumeralMapped[i] = !search ;
  531.     }
  532.  
  533.     tstr[1] = '\0' ;
  534.     /* add one number values as possible */
  535.     /* we rely on the fact that the first word in the single letter word
  536.      * lists is actually a numeral, so change the following at your own
  537.      * peril.
  538.      */
  539.     for ( i = 0 ; i < 10 ; i++ ) {
  540.     tstr[0] = (char)i + '0' ;
  541.     (void)fit_word( tstr, 1 ) ;
  542.     }
  543.  
  544.     /* add one letter words as possible */
  545.     length = strlen( OneLetter ) ;
  546.     for ( i = 0 ; i < length ; i++ ) {
  547.     tstr[0] = OneLetter[i] ;
  548.     (void)fit_word( tstr, 1 ) ;
  549.     }
  550. }
  551.  
  552. /* see if the word fits in the database anywhere */
  553. int
  554. fit_word( dict_word, min_length )
  555. char    *dict_word ;
  556. int    min_length ;
  557. {
  558. int    length, compare_length, true_length, index_length, nl ;
  559. int    i, j ;
  560. int    word_val[MAX_DIGITS], hit[MAX_DIGITS], *wv, check_length, match, tot ;
  561. char    lc_dict_word[MAX_DIGITS], *wc, *lc, tchr, *cc ;
  562. char    clean_dict_word[BUFSIZE] ;
  563.  
  564.     /* convert word to numbers */
  565.     length = strlen( dict_word ) ;
  566.     compare_length = true_length = 0 ;
  567.     for ( i = 0, wv = word_val, wc = dict_word, lc = lc_dict_word,
  568.       cc = clean_dict_word
  569.     ; ( i < length ) && ( compare_length < NumDigits )
  570.     ; i++, wc++ ) {
  571.  
  572.     /* remove apostrophes, hyphens, etc for matching */
  573.     tchr = tolower(*wc) ;
  574.     if ( ( tchr >= 'a' ) && ( tchr <= 'z' ) ) {
  575.         *wv++ = Letter2Numeral[(*lc++ = tchr) -'a'] ;
  576.         compare_length++ ;
  577.     } else if ( ( tchr >= '0' ) && ( tchr <= '9' ) ) {
  578.         *wv++ = (*lc++ = tchr) -'0' ;
  579.         compare_length++ ;
  580.     }
  581.  
  582.     /* clean the word of any carriage return junk */
  583.     if ( tchr > 13 ) {
  584.         *cc++ = *wc ;
  585.     }
  586.     }
  587.     true_length = compare_length ;
  588.  
  589.     while ( *wc ) {
  590.     tchr = tolower( *wc++ ) ;
  591.     /* get true length of word in valid characters */
  592.     if ( ( tchr >= 'a' ) && ( tchr <= 'z' ) ) {
  593.         true_length++ ;
  594.     } else if ( ( tchr >= '0' ) && ( tchr <= '9' ) ) {
  595.         true_length++ ;
  596.     }
  597.     /* copy the rest of the word */
  598.     if ( tchr > 13 ) {
  599.         *cc++ = tchr ;
  600.     }
  601.     }
  602.     /* end the cleaned word */
  603.     *cc = '\0' ;
  604.  
  605.     /* is the word too long? */
  606.     if ( true_length > NumDigits + Wildcard ) {
  607.     /* the word was too long */
  608.     return( 0 ) ;
  609.     }
  610.  
  611.     /* is the word too short? */
  612.     if ( true_length < min_length ) {
  613.     /* the word was too short */
  614.     return( 0 ) ;
  615.     }
  616.  
  617.     /* now look for matches */
  618.     if ( Wildcard ) {
  619.     check_length = NumDigits - true_length + Wildcard ;
  620.     if ( check_length >= NumDigits ) {
  621.         /* cannot index into array at higher than NumDigits-1, so reduce */
  622.         check_length = NumDigits - 1 ;
  623.     }
  624.     } else {
  625.     check_length = NumDigits - true_length ;
  626.     }
  627.  
  628.     for ( i = 0, tot = 0 ; i <= check_length ; i++ ) {
  629.     index_length = compare_length+i ;
  630.     if ( Wildcard ) {
  631.         if ( index_length > NumDigits ) {
  632.         /* cut comparisons down to numerals available */
  633.         index_length = NumDigits ;
  634.         }
  635.     }
  636.     for ( j = i, wv = word_val, match = TRUE
  637.         ; match && (j < index_length)
  638.         ; j++, wv++ ) {
  639.  
  640.         /* numerical match? */
  641.         if ( PhoneNum[j] != *wv ) {
  642.         /* no; exact letter match? */
  643.         if ( ( PhoneNum[j] != FIXED_LETTER ) ||
  644.              ( PhoneStr[j] != lc_dict_word[j-i] ) ) {
  645.             /* no match, stop testing */
  646.             match = FALSE ;
  647.         }
  648.         }
  649.     }
  650.     if ( match ) {
  651.         /* word fits, store index */
  652.         hit[tot++] = i ;
  653.     }
  654.     }
  655.     /* were there any matches? */
  656.     if ( tot ) {
  657.     /* make one copy of the word */
  658.     wc = strdup( clean_dict_word ) ;
  659.     hold_word( wc ) ;
  660.     for ( i = 0 ; i < tot ; i++ ) {
  661.         if ( Wildcard ) {
  662.         if ( hit[i] + true_length <= NumDigits ) {
  663.             nl = true_length ;
  664.         } else {
  665.             nl = NumDigits - hit[i] ;
  666.         }
  667.         store_word( wc, hit[i], nl ) ;
  668.         } else {
  669.         store_word( wc, hit[i], true_length ) ;
  670.         }
  671.     }
  672.     }
  673.  
  674.     return( tot ) ;
  675. }
  676.  
  677. /* yeah, I could combine this with store_word - call me lazy... */
  678. void
  679. hold_word( word )
  680. char    *word ;
  681. {
  682.     if ( HoldSize <= HoldCount ) {
  683.     if ( HoldSize ) {
  684.         HoldSize += WORD_LIST_SIZE * NumDigits ;
  685.         HoldWord = (char **)realloc( (void *)HoldWord,
  686.             HoldSize * sizeof(char *)) ;
  687.     } else {
  688.         HoldSize = WORD_LIST_SIZE * NumDigits ;
  689.         HoldWord =
  690.             (char **)malloc( HoldSize * sizeof(char *)) ;
  691.     }
  692.     if ( HoldWord == NULL ) {
  693.         fprintf( stderr, "Ugh, we're out of memory!\n" ) ;
  694.         exit(1) ;
  695.     }
  696.     }
  697.     HoldWord[HoldCount++] = word ;
  698. }
  699.  
  700. /* store word in the given location */
  701. void
  702. store_word( word, digit, length )
  703. char    *word ;
  704. int    digit ;
  705. int    length ;
  706. {
  707. p_wordlist    p_wl ;
  708.  
  709.     p_wl = &WL[IndexWL( digit, length )] ;
  710.     /* check storage space */
  711.     if ( p_wl->size <= p_wl->count ) {
  712.     if ( p_wl->size ) {
  713.         p_wl->size += WORD_LIST_SIZE ;
  714.         p_wl->p_word = (char **)realloc( (void *)p_wl->p_word,
  715.             p_wl->size * sizeof(char *)) ;
  716.     } else {
  717.         p_wl->size = WORD_LIST_SIZE ;
  718.         p_wl->p_word =
  719.             (char **)malloc( p_wl->size * sizeof(char *)) ;
  720.     }
  721.     if ( p_wl->p_word == NULL ) {
  722.         fprintf( stderr, "Ugh, we're out of memory!\n" ) ;
  723.         exit(1) ;
  724.     }
  725.     }
  726.     /* store word in structure */
  727.     p_wl->p_word[p_wl->count++] = word ;
  728. }
  729.  
  730. /* search through the stored words for matches */
  731. void
  732. search_for_match( digit, full_string, suffix_loc, numeral_count )
  733. int    digit ;
  734. char    *full_string ;
  735. char    *suffix_loc ;
  736. int    numeral_count ;
  737. {
  738. int    length, tot_len, tot_word, wn, add_num, add_val ;
  739. p_wordlist    p_wl ;
  740. char    **p_word ;
  741.  
  742.     tot_len = NumDigits - digit ;
  743.     /* loop through all possible word lengths from this point */
  744.     /* count down so that the longer strings are output first */
  745.     for ( length = tot_len ; length > 0 ; length-- ) {
  746.     p_wl = &WL[IndexWL( digit, length )] ;
  747.     tot_word = p_wl->count ;
  748.     /* now go through all words on the list */
  749.     if ( length == 1 ) {
  750.         /* is the numeral of the first word mapped? */
  751.         if ( NumeralMapped[**(p_wl->p_word)-'0'] ) {
  752.         /* don't include first word (a mapped numeral) if we've used
  753.          * up our quota.
  754.          */
  755.         wn = (numeral_count >= NumNumerals ) ;
  756.         add_num = 1 ;
  757.         } else {
  758.         /* an unmapped numeral, so definitely do it */
  759.         wn = 0 ;
  760.         add_num = 0 ;
  761.         }
  762.     } else {
  763.         /* not on the one letter word list, so do all words */
  764.         wn = 0 ;
  765.         add_num = 0 ;
  766.     }
  767.     for ( p_word = p_wl->p_word + wn
  768.         ; wn < tot_word
  769.         ; wn++, p_word++ ) {
  770.  
  771.         strcpy( suffix_loc, *p_word ) ;
  772.         if ( length == tot_len ) {
  773.         /* finished - output it! */
  774.         fprintf( stdout, "%s\n", full_string ) ;
  775.         MatchTot++ ;
  776.         } else {
  777.         strcat( suffix_loc, " " ) ;
  778.         /* Add one to numeral_count only if numeral is used */
  779.         search_for_match( digit+length, full_string,
  780.                 suffix_loc + strlen( suffix_loc ),
  781.             numeral_count + add_num ) ;
  782.         }
  783.     }
  784.     }
  785. }
  786.  
  787. void
  788. free_wl()
  789. {
  790. int i ;
  791. p_wordlist p_wl ;
  792.  
  793.     for ( i = 0, p_wl = WL ; i < NumIndices ; i++, p_wl++ ) {
  794.     if ( p_wl->size ) {
  795.         p_wl->size = 0 ;
  796.         p_wl->count = 0 ;
  797.         free( p_wl->p_word ) ;
  798.     }
  799.     }
  800.  
  801.     /* the only function of HoldWord is to be able to free the word memory */
  802.     if ( HoldSize ) {
  803.     for ( i = 0 ; i < HoldCount ; i++ ) {
  804.         free( HoldWord[i] ) ;
  805.     }
  806.     free( HoldWord ) ;
  807.     }
  808.     HoldCount = HoldSize = 0 ;
  809. }
  810.  
  811. /* strip out all valid "-" arguments and their values */
  812. int
  813. scan_options( argc, argv, nargc, nargv )
  814. int    argc ;
  815. char    *argv[] ;
  816. int    *nargc ;
  817. char    *nargv[] ;
  818. {
  819. int    num_arg ;
  820. int    i ;
  821. char    str[BUFSIZE] ;
  822.  
  823.     *nargc = num_arg = 0 ;
  824.  
  825.     while ( ++num_arg < argc ) {
  826.     if ( *argv[num_arg] == '-' ) {
  827.         switch( argv[num_arg][1] ) {
  828.         case 'l':    /* minimum length of words */
  829.             if ( ++num_arg < argc ) {
  830.             sscanf( argv[num_arg], "%d", &i ) ;
  831.             if ( i < 1 ) {
  832.                 fprintf( stderr,
  833.                     "Minimum word length too low!\n" ) ;
  834.                 USAGE ;
  835.                 return( FALSE ) ;
  836.             }
  837.             MinLength = i ;
  838.             }
  839.             break ;
  840.         case 'n':    /* number of numerals allowed */
  841.             if ( ++num_arg < argc ) {
  842.             sscanf( argv[num_arg], "%d", &i ) ;
  843.             if ( i < 0 ) {
  844.                 fprintf( stderr, "Number of numerals too low!\n" ) ;
  845.                 USAGE ;
  846.                 return( FALSE ) ;
  847.             }
  848.             NumNumerals = i ;
  849.             }
  850.             break ;
  851.         case 'q':    /* mapping of q */
  852.             if ( ++num_arg < argc ) {
  853.             sscanf( argv[num_arg], "%d", &i ) ;
  854.             if ( (i >= 0) && (i <= 9) ) {
  855.                 Letter2Numeral['q'-'a'] = i ;
  856.             }
  857.             }
  858.             break ;
  859.         case 'z':    /* mapping of z */
  860.             if ( ++num_arg < argc ) {
  861.             sscanf( argv[num_arg], "%d", &i ) ;
  862.             if ( (i >= 0) && (i <= 9) ) {
  863.                 Letter2Numeral['z'-'a'] = i ;
  864.             }
  865.             }
  866.             break ;
  867.         case 'd':    /* dictionary path */
  868.             if ( ++num_arg < argc ) {
  869.             sscanf( argv[num_arg], "%s", DictPath ) ;
  870.             }
  871.             break ;
  872.         case 's':    /* allowed single letter words */
  873.             if ( ++num_arg < argc ) {
  874.             sscanf( argv[num_arg], "%s", OneLetter ) ;
  875.             }
  876.             break ;
  877.         case 'm':    /* mapping of entire alphabet */
  878.             if ( ++num_arg < argc ) {
  879.             sscanf( argv[num_arg], "%s", str ) ;
  880.             if ( strlen(str) != 26 ) {
  881.                 fprintf( stderr, "Must input all 26 digits\n" ) ;
  882.                 USAGE ;
  883.                 return( FALSE ) ;
  884.             }
  885.             for ( i = 0 ; i < 26 ; i++ ) {
  886.                 if ( (str[i] >= '0') && (str[i] <= '9') ) {
  887.                 Letter2Numeral[i] = str[i] - '0' ;
  888.                 } else {
  889.                 Letter2Numeral[i] = NO_NUMBER ;
  890.                 }
  891.             }
  892.             if ( Verbose ) {
  893.                 fprintf( stderr, "here's your banana: )\n" ) ;
  894.             }
  895.             }
  896.             break ;
  897.         case 'r':    /* output concatenation */
  898.             RollOwn = TRUE ;
  899.             break ;
  900.         case 'c':    /* output concatenation */
  901.             Concat = 1 ;
  902.             break ;
  903.         case 'C':    /* output spacing & concatenation */
  904.             Concat = 2 ;
  905.             break ;
  906.         case 'v':    /* verbose output */
  907.             Verbose = TRUE ;
  908.             break ;
  909.         default:
  910.             fprintf( stderr, "No such option `-%c'\n",
  911.                 argv[num_arg][1] ) ;
  912.             USAGE ;
  913.             return( FALSE ) ;
  914.         }
  915.     } else {
  916.         /* not an argument, so pass it on */
  917.         nargv[(*nargc)++] = argv[num_arg] ;
  918.     }
  919.     }
  920.     return( TRUE ) ;
  921. }
  922.